package square;

import be.kuleuven.cs.som.annotate.Model;


/**
 * An enumeration introducing different scales to
 * express the temperature.
 * 		In its current form, the class only supports 
 * 		Celcius, Fahrenheit and Kelvin.	
 * 
 * @version 2.0
 * @author	Inge Gielis <inge.gielis@student.kuleuven.be>
 * @author 	Annelies Ketelslegers <annelies.ketelslegers@student.kuleuven.be>
 */
public enum TemperatureScale {
	
	CELCIUS{
		
		/**
		 * Return the value of the given temperature in the given scale.
		 * 
		 * @param	temperature
		 * 			The temperature in degrees Celcius that must be converted.
		 * @param	newScale
		 * 			The temperature scale to which the temperature must be converted.
		 * @effect	If the given temperature scale is Kelvin, the temperature will be converted to this scale
		 * 			| result == convertCelciusToKelvin(temperature)
		 * @effect	If the given temperature scale is Fahrenheit, the temperature will be converted to this scale.
		 * 			| convertCelciusToFahrenheit(temperature)
		 * @return	If the given temperature scale is Celcius, the given temperature value will be returned.
		 * 			| result == temperature
		 * @throws 	IllegalArgumentException
		 * 			The value for the given temperature is not a number or is infinite.
		 * 			| (Double.isNaN(temperature)) || (Double.isInfinite(temperature)
		 * @throws 	IllegalArgumentException
		 * 			The temperature scale is not a valid scale
		 * 			|newScale != CELCIUS && newScale != FAHRENHEIT && newScale != KELVIN??
		 * @throws 	OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		public double convertTo(double temperature, TemperatureScale newScale)
		throws OverflowException, IllegalArgumentException{
			if((Double.isNaN(temperature)) || (Double.isInfinite(temperature)))
				throw new IllegalArgumentException("Not a valid temperaturevalue");
			switch(newScale){
			case FAHRENHEIT:
				return convertCelciusToFahrenheit(temperature);
			case KELVIN:
				return convertCelciusToKelvin(temperature);
			case CELCIUS:
				return temperature;
			default:
				throw new IllegalArgumentException("Not a valid temperatureScale");
			}	
		}
		
		/**
		 * Return the temperature in degrees Fahrenheit.
		 * 
		 * @param 	celciusTemperature
		 * 			The value for the temperature in the celcius scale
		 * @return	The temperature value in the Fahrenheit scale.
		 * @throws 	OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		@Model 
		private double convertCelciusToFahrenheit (double celciusTemperature) throws OverflowException{
			double newTemperature = ExtMath.times(celciusTemperature, 1.8);
			newTemperature = ExtMath.add(newTemperature, 32);
			return newTemperature;	
		}
		
		/**
		 * Return the temperature in Kelvin.
		 * 
		 * @param 	celciusTemperature
		 * 			The value for the temperature in the celcius scale
		 * @return	The temperature value in the Kelvin scale.
		 * @throws 	OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		@Model
		private double convertCelciusToKelvin(double celciusTemperature) throws OverflowException{
			double newTemperature = ExtMath.add(celciusTemperature, 273.15);
			return newTemperature;	
		}
		
	}, FAHRENHEIT {
		
		/**
		 * Return the value of the given temperature in the given scale.
		 * 
		 * @param	temperature
		 * 			The temperature in degrees Fahrenheit that must be converted.
		 * @param	newScale
		 * 			The temperature scale to which the temperature must be converted.
		 * @effect	If the given temperature scale is Kelvin, the temperature will be converted to this scale
		 * 			| result == convertFahrenheitToKelvin(temperature)
		 * @effect	If the given temperature scale is Celcius, the temperature will be converted to this scale.
		 * 			| result == convertFahrenheitToCelcius(temperature)
		 * @return	If the given temperature scale is Fahrenheit, the given temperature value will be returned.
		 * 			| result == temperature
		 * @throws 	IllegalArgumentException
		 * 			The value for the given temperature is not a number or is infinite.
		 * 			| (Double.isNaN(temperature)) || (Double.isInfinite(temperature)
		 * @throws 	IllegalArgumentException
		 * 			The temperature scale is not a valid scale
		 * 			|newScale != CELCIUS && newScale != FAHRENHEIT && newScale != KELVIN??
		 * @throws OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		public double convertTo(double temperature, TemperatureScale newScale)
				throws OverflowException, IllegalArgumentException {
			if((Double.isNaN(temperature)) || (Double.isInfinite(temperature)))
				throw new IllegalArgumentException("Not a valid temperaturevalue");
			switch(newScale){
			case FAHRENHEIT:
				return temperature;
			case KELVIN:
				return convertFahrenheitToKelvin(temperature);
			case CELCIUS:
				return convertFahrenheitToCelcius(temperature);
			default:
				throw new IllegalArgumentException("Not a valid temperatureScale");
			}
		}
		
		/**
		 * Return the temperature value in degrees Celcius.
		 * 
		 * @param 	fahrenheitTemperature
		 * 			The value for the temperature in the Fahrenheit scale
		 * @return	The temperature value in the Celcius scale.
		 * @throws 	OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		@Model
		private double convertFahrenheitToCelcius(double fahrenheitTemperature) throws OverflowException{
			double newTemperature = ExtMath.add(fahrenheitTemperature, -32);
			newTemperature = ExtMath.times(newTemperature, 5/9);
			return newTemperature;
		}
		
		/**
		 * Return the temperature value in degrees Kelvin.
		 * 
		 * @param 	fahrenheitTemperature
		 * 			The value for the temperature in the fahrenheit scale
		 * @return	The temperature value in the Kelvin scale.
		 * @throws 	OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		@Model
		private double convertFahrenheitToKelvin(double fahrenheitTemperature) throws OverflowException{
			double newTemperature = ExtMath.add(fahrenheitTemperature, 459.67);
			newTemperature = ExtMath.times(newTemperature, 5/9);
			return newTemperature;
		}
		
	}, KELVIN {
		
		/**
		 * Return the value of the given temperature in the given scale.
		 * 
		 * @param	temperature
		 * 			The temperature in Kelvin that must be converted.
		 * @param	newScale
		 * 			The temperature scale to which the temperature must be converted.
		 * @effect	If the given temperature scale is FahrenHeit, the temperature will be converted to this scale
		 * 			| result == convertKelvinToFahrenheit(temperature)
		 * @effect	If the given temperature scale is Celcius, the temperature will be converted to this scale.
		 * 			| result == convertKelvinToCelcius(temperature)
		 * @return	If the given temperature scale is Kelvin, the given temperature value will be returned.
		 * 			| result == temperature
		 * @throws 	IllegalArgumentException
		 * 			The value for the given temperature is not a number or is infinite.
		 * 			| (Double.isNaN(temperature)) || (Double.isInfinite(temperature)
		 * @throws 	IllegalArgumentException
		 * 			The temperature scale is not a valid scale
		 * 			|newScale != CELCIUS && newScale != FAHRENHEIT && newScale != KELVIN??
		 * @throws OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		public double convertTo(double temperature,
				 TemperatureScale newScale)
				throws OverflowException, IllegalArgumentException {
			if((Double.isNaN(temperature)) || (Double.isInfinite(temperature)))
				throw new IllegalArgumentException("Not a valid temperaturevalue");
			switch(newScale){
			case FAHRENHEIT:
				return convertKelvinToFahrenheit(temperature);
			case KELVIN:
				return temperature;
			case CELCIUS:
				return convertKelvinToCelcius(temperature);
			default:
				throw new IllegalArgumentException("Not a valid temperatureScale");
			}
		}
		
		/**
		 * Return the temperature value in degrees Celcius.
		 * 
		 * @param 	kelvinTemperature
		 * 			The value for the temperature in the Kelvin scale
		 * @return	The temperature value in the Celcius scale.
		 * @throws 	OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		@Model
		private double convertKelvinToCelcius(double kelvinTemperature) throws OverflowException{
			double newTemperature = ExtMath.add(kelvinTemperature, -273.15);
			return newTemperature;
		}
		
		/**
		 * Return the temperature value in degrees Fahrenheit.
		 * 
		 * @param 	kelvinTemperature
		 * 			The value for the temperature in the Kelvin scale
		 * @return	The temperature value in the Fahrenheit scale.
		 * @throws 	OverflowException
		 * 			The given temperature is too high, converting the temperature would cause overflow.
		 */
		@Model
		private double convertKelvinToFahrenheit(double kelvinTemperature) throws OverflowException{
			double newTemperature = ExtMath.times(kelvinTemperature, 1.8);
			newTemperature = ExtMath.add(newTemperature, -459.67);
			return newTemperature;
		}
	};
	
	
	/**
	 * Return the value of the temperature in this scale converted to the other scale.
	 * 
	 * @param 	temperature
	 * 			The temperature to convert.
	 * @param 	newScale
	 * 			The temperature scale to convert to
	 * @throws 	IllegalArgumentException
	 * 			The value for the given temperature is not a number or is infinite.
	 * 			| (Double.isNaN(temperature)) || (Double.isInfinite(temperature)
	 * @throws	IllegalArgumentException
	 * 			The given temperature scale is not a valid scale, for instance null. 
	 * @throws 	OverflowException
	 * 			The given temperature is too high, converting the temperature would cause overflow.
	 */
	public abstract double convertTo(double temperature, TemperatureScale newScale)
				throws OverflowException, IllegalArgumentException;

}
